package obecto.task.container
{
	import flash.utils.Dictionary;
	
	import obecto.event.EventDescriptor;
	import obecto.event.TaskContainerEvent;
	import obecto.event.TaskEvent;
	import obecto.task.BaseTask;
	import obecto.task.ITask;
	import obecto.utility.Listener;

	[DefaultProperty("tasks")]
	public class Parallel extends BaseTask implements ITaskContainer, IMxmlTaskContainer
	{
		protected var _tasks : Dictionary = new Dictionary();
		
		protected static const COMPLETE_KEY : String = "complete";
		protected static const LISTENER_KEY : String = "listener"; 
		
		public function addTask(task : ITask) : void
		{
			assertObjectIsNotNull(task);
			
			if (_tasks[task] == undefined)
			{
				_tasks[task] = new Object();
				resetTask(task);
			}
			
			if (running == true)
			{
				_tasks[task][LISTENER_KEY] = 
					Listener.createSingleTimeListener(task, TaskEvent.COMPLETE, handleTaskComplete, true);
					
				runTask(task);
			}
			
			dispatchEvent(new TaskContainerEvent(TaskContainerEvent.TASK_ADDED, task, null));
		}
		
		public function removeTask(task : ITask) : void
		{
			assertObjectIsNotNull(task);
			
			if (_tasks[task] != undefined)
			{
				resetTask(task);
				delete _tasks[task];
				
				if (running == true && allTasksComplete == true)
				{
					finish();
				}				
			}
			
			dispatchEvent(new TaskContainerEvent(TaskContainerEvent.TASK_REMOVED, task, null));
		}
		
		public function getAllTasks() : Array
		{
			return tasks;
		}
		
		public function set tasks(value : Array) : void
		{
			var task : Object;
			
			resetTasks();
			
			for (task in _tasks)
			{
				delete _tasks[task];
			}
			
			for each (task in value)
			{
				assertObjectIsNotNull(task);
				assertObjectIsTask(task);
				_tasks[task] = new Object();
			}
			
			resetTasks();
			
			if (running == true)
			{
				_running = false;
				run();
			}
		}
		
		public function get tasks() : Array
		{
			var result : Array = new Array();
			
			for (var task : Object in _tasks)
			{
				result.push(task);
			}
			
			return result;
		}
		
		override public function start() : void
		{
			if (running == false)
			{
				super.start();
				
				if (tasks.length == 0)
				{
					complete();
				}
				else
				{
					resetTasks();
					
					for (var task : Object in _tasks)
					{
						_tasks[task][LISTENER_KEY] = 
							Listener.createSingleTimeListener(task as ITask, TaskEvent.COMPLETE, handleTaskComplete, true);
							
						(task as ITask).start();
					}
				}
			}
		}
		
		protected function handleTaskComplete(event : TaskEvent) : void
		{
			_tasks[event.target][COMPLETE_KEY] = true;
			_tasks[event.target][LISTENER_KEY] = null;
			
			if (allTasksComplete)
			{
				finish();
			}
		}
		
		protected function get allTasksComplete() : Boolean
		{
			for (var task : Object in _tasks)
			{
				if (_tasks[task][COMPLETE_KEY] == false)
				{
					return false;
				}
			}
			
			return true;
		}
		
		public function resetTasks() : void
		{
			for (var task : Object in _tasks)
			{
				resetTask (task as ITask);	
			}
		}
		
		protected function resetTask(task : ITask) : void
		{
			_tasks[task][COMPLETE_KEY] = false;
			
			if (_tasks[task][LISTENER_KEY] != null && _tasks[task][LISTENER_KEY] != undefined)
			{
				if ((_tasks[task][LISTENER_KEY] as Listener).listening == true)
				{
					(_tasks[task][LISTENER_KEY] as Listener).stop();
				}
				
				_tasks[task][LISTENER_KEY] = null;
			}
		}
		
		protected function assertObjectIsNotNull(object : Object) : void
		{
			if (object == null)
			{
				throw new Error("Object can't be null.");
			}
		}
		
		protected function assertObjectIsTask(object : Object) : void
		{
			if ((object is ITask) == false)
			{
				throw new Error("Object isn't task.");
			}				
		}
		
		protected function run() : void
		{
			start();
		}
		
		protected function finish() : void
		{
			complete();
		}
		
		protected function runTask(task : ITask) : void
		{
			task.start();
		}			
	}
}